package com.yao2san.simdif.service.synchronizer;

import cn.hutool.core.util.BooleanUtil;
import com.yao2san.simdif.entity.ColumnInfo;
import com.yao2san.simdif.entity.TableInfo;
import com.yao2san.simdif.enums.Drivers;
import lombok.extern.slf4j.Slf4j;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Mysql数据源同步
 *
 * @author wxg
 */
@Slf4j
public class MysqlDatasourceSynchronizer extends AbstractDatasourceSynchronizer {

    private static final String SQL_QUERY_TABLE = "select * from information_schema.tables where table_schema = ?";
    private static final String SQL_QUERY_COLUMN = "select *  from information_schema.columns where table_schema = ? and table_name=?";
    private static final String SQL_QUERY_INDEX = "select * " +
            "from (SELECT t.TABLE_SCHEMA, " +
            "             t.TABLE_NAME, " +
            "             t.INDEX_NAME, " +
            "             GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX) AS `COLUMNS` " +
            "      FROM information_schema.statistics t " +
            "      GROUP BY t.TABLE_SCHEMA, t.TABLE_NAME, t.INDEX_NAME) as tb where tb.TABLE_SCHEMA =?";

    public MysqlDatasourceSynchronizer(String url, String user, String password) {
        super(url, user, password);
    }


    @Override
    public List<TableInfo> getTables(Connection connection, String database) {
        List<TableInfo> allTables = getAllTables(connection, database);
        log.info("Find {} tables", allTables.size());
        Map<String, List<String>> allTableIndexes = getAllTableIndexes(connection, database);
        log.info("Find {} indexes", allTableIndexes.size());
        for (TableInfo tableInfo : allTables) {
            List<String> indexes = allTableIndexes.get(tableInfo.getTableName());
            if (indexes != null) {
                tableInfo.setTableIndex(String.join("; ", indexes));
            }
        }
        return allTables;
    }

    private List<TableInfo> getAllTables(Connection connection, String database) {
        PreparedStatement statement;
        try {
            statement = connection.prepareStatement(SQL_QUERY_TABLE);

            statement.setString(1, database);
            ResultSet resultSet = statement.executeQuery();
            List<TableInfo> tableInfos = new ArrayList<>();
            while (resultSet.next()) {
                TableInfo tableInfo = new TableInfo();
                tableInfo.setTableName(resultSet.getString("TABLE_NAME"));
                tableInfo.setTableType(resultSet.getString("TABLE_TYPE"));
                tableInfo.setTableRows(resultSet.getLong("TABLE_ROWS"));
                tableInfo.setTableComment(resultSet.getString("TABLE_COMMENT"));
                tableInfo.setTableCollation(resultSet.getString("TABLE_COLLATION"));
                tableInfo.setTableCreateTime(resultSet.getDate("CREATE_TIME"));
                tableInfo.setTableUpdateTime(resultSet.getDate("UPDATE_TIME"));
                tableInfo.setTableEngine(resultSet.getString("ENGINE"));
                tableInfos.add(tableInfo);
            }
            return tableInfos;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    private Map<String, List<String>> getAllTableIndexes(Connection connection, String database) {
        Map<String, List<String>> indexMap = new HashMap<>();
        PreparedStatement statement;
        try {
            statement = connection.prepareStatement(SQL_QUERY_INDEX);
            statement.setString(1, database);
            ResultSet indexResultSet = statement.executeQuery();
            while (indexResultSet.next()) {
                String tableName = indexResultSet.getString("TABLE_NAME");
                String indexName = indexResultSet.getString("INDEX_NAME");
                String indexColumn = indexResultSet.getString("COLUMNS");
                List<String> list = indexMap.computeIfAbsent(tableName, k -> new ArrayList<>());
                list.add(indexName + "(" + indexColumn + ")");
            }
            return indexMap;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public List<ColumnInfo> getColumns(Connection connection, String database, String tableName) {
        List<ColumnInfo> columnInfos = new ArrayList<>();
        PreparedStatement statement;
        try {
            statement = connection.prepareStatement(SQL_QUERY_COLUMN);

            statement.setString(1, database);
            statement.setString(2, tableName);
            ResultSet resultSet = statement.executeQuery();

            while (resultSet.next()) {
                ColumnInfo columnInfo = new ColumnInfo();
                columnInfo.setColumnName(resultSet.getString("COLUMN_NAME"));
                columnInfo.setColumnSort(resultSet.getInt("ORDINAL_POSITION"));
                columnInfo.setColumnDefault(resultSet.getObject("COLUMN_DEFAULT"));
                columnInfo.setColumnDataType(resultSet.getString("DATA_TYPE"));
                columnInfo.setColumnType(resultSet.getString("COLUMN_TYPE"));
                columnInfo.setColumnIndex(resultSet.getString("COLUMN_KEY"));
                columnInfo.setColumnNull(BooleanUtil.toBoolean(resultSet.getString("IS_NULLABLE")));
                columnInfo.setColumnComment(resultSet.getString("COLUMN_COMMENT"));
                columnInfo.setTableName(resultSet.getString("TABLE_NAME"));
                columnInfos.add(columnInfo);
            }
            return columnInfos;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public String getDriverFile() {
        return Drivers.MYSQL.getDriver();
    }

    @Override
    public String getDriverClassName() {
        return Drivers.MYSQL.getDriverClassName();
    }

}
